using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml;

namespace SocketClient {

    public class ModbusReplyItem : ReplyItem {
        public ModbusReplyItem() {
            this.Name = "modbusReplyItem";
        }

        public byte[] ReplyBytesTemplate { get; set; }

        private byte[] _last;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public override bool Match(byte[] bytes) {
            _last = bytes;
            return IsModbusPackage(bytes);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private bool IsModbusPackage(byte[] bytes) {
            return IsModbusReadBitPackage(bytes) || IsModbusReadWordPackage(bytes);
        }

        private bool IsModbusReadBitPackage(byte[] bytes) {
            if (bytes != null && bytes.Length == 8) {
                var fc = bytes[1];
                if (fc == 0x02) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private bool IsModbusReadWordPackage(byte[] bytes) {
            if (bytes != null && bytes.Length == 8) {
                var fc = bytes[1];
                if (fc == 0x03 || fc == 0x04) {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override byte[] GetReplyBytes() {
            if (IsModbusReadBitPackage(_last)) {
                return GetBitReplyBytes();
            }

            if (IsModbusReadWordPackage(_last)) {
                return GetWordReplyBytes();
            }

            return new byte[] { };
            //throw new InvalidOperationException("get reply bytes error: " + BitConverter.ToString(_last));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private byte[] GetBitReplyBytes(){
            // word count - bigendian int 16
            //
            var bsWord = new byte[] { _last[5], _last[4]};
            var bitCount = BitConverter.ToInt16(bsWord, 0);
            var charCount = bitCount / 8 + ((bitCount % 8) > 0 ? 1 : 0);
            var r = new List<byte>();
            r.Add(_last[0]);
            r.Add(_last[1]);
            r.Add((byte)charCount);


            if (this.ReplyBytesTemplate == null || this.ReplyBytesTemplate.Length == 0) {
                for (byte i = 0; i < charCount; i++) {
                    r.Add(0x0f);
                }
            } else {
                // has template
                int idxTemplate = 0;
                for (byte i = 0; i < charCount; i++) {
                    r.Add(this.ReplyBytesTemplate[idxTemplate]);
                    idxTemplate++;
                    idxTemplate = this.ReplyBytesTemplate.Length == idxTemplate ? 0 : idxTemplate;
                }
            }

            var bsCrc = CRC16.CalculateCRC(r.ToArray());
            r.AddRange(bsCrc);
            return r.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private byte[] GetWordReplyBytes(){
            // word count - bigendian int 16
            //
            var bsWord = new byte[] { _last[5], _last[4]};
            var wordCount = BitConverter.ToInt16(bsWord, 0);
            var charCount = wordCount * 2;
            var r = new List<byte>();
            r.Add(_last[0]);
            r.Add(_last[1]);
            r.Add((byte)charCount);
            if (this.ReplyBytesTemplate == null || this.ReplyBytesTemplate.Length == 0) {
                for (byte i = 0; i < charCount; i++) {
                    r.Add(i);
                }
            } else {
                // has template
                int idxTemplate = 0;
                for (byte i = 0; i < charCount; i++) {
                    r.Add(this.ReplyBytesTemplate[idxTemplate]);
                    idxTemplate++;
                    idxTemplate = this.ReplyBytesTemplate.Length == idxTemplate ? 0 : idxTemplate;
                }
            }

            var bsCrc = CRC16.CalculateCRC(r.ToArray());
            r.AddRange(bsCrc);
            return r.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="reply"></param>
        public override void SetReplyBytes(byte[] reply) {
            throw new NotSupportedException();
        }
    }
}
